#!/bin/sh
. /usr/share/libubox/jshn.sh

# compare_pid process_id pid
# 当process_id与pid相等，或process_id是pid子进程时返回0，否则返回1
compare_pid() {
	local process_id=$1
	local pid=$2
	local p
	[ -n "$pid" ] && [ -n "$process_id" ] || return 1
	[ "$process_id" -eq "$pid" ] && return 0
	for p in $(busybox pgrep -P "$pid"); do
		compare_pid "$process_id" "$p" && return 0
	done
	return 1
}

# stop_occupying_service mount_point
# 停止所有占用挂载点的服务
# example: stop_occupying_service /mnt/sda1
stop_occupying_service() {
	local mountpoint process_id services service instances instance kill_flag pid running

	json_init
	json_load "$(ubus call service list)"
	json_get_keys services

	mountpoint="$1"
	for process_id in $(lsof -t "$mountpoint"); do
		for service in $services; do
			kill_flag=0
			if json_is_a $service object; then
				json_select $service
				if json_is_a instances object; then
					json_select instances
					json_get_keys instances
					for instance in $instances; do
						if json_is_a $instance object; then
							json_select $instance
							json_get_var pid pid
							json_get_var running running
							[ -n "$running" ] && [ "$running" -eq 1 ] &&
								compare_pid "$process_id" "$pid" && kill_flag=1
							json_select ..
						fi
					done
					json_select ..
				fi
				json_select ..
			fi
			[ $kill_flag -eq 1 ] && {
				echo "force-unmount: stopping service: ${service}" >&2
				/etc/init.d/$service stop
			}
		done
	done
}

# kill_occupying_process mount_point
# 结束所有占用挂载点的进程
# example: kill_occupying_process /mnt/sda1
kill_occupying_process() {
	local mountpoint process_id
	mountpoint="$1"
	for process_id in $(lsof -t "$mountpoint"); do
		echo "force-unmount: killing process: $(cat /proc/$process_id/comm)" >&2
		kill -9 $process_id
	done
}

MAX_TRY=5

[ "$#" -eq 1 ] || exit 1

# 去除挂载点路径中连续多余的'/'，例如 /mnt//sda1 -> /mnt/sda1
# 去除挂载点路径末尾的'/'，例如 /mnt/sda1/ -> /mnt/sda1
mountpoint="$(echo "$1" | sed -e 's#/\+#/#g' -e 's#/*[[:space:]]*$##g')"

[ -e "$mountpoint" ] || {
	echo "force-unmount: can't unmount ${mountpoint}: No such file or directory" >&2
	exit 1
}

if awk '{print $2}' /proc/mounts | grep -q "^${mountpoint}$"; then
	for try_count in $(seq $MAX_TRY); do
		echo "force-unmount: unmounting ${mountpoint}: try count: ${try_count}" >&2
		stop_occupying_service "$mountpoint"
		kill_occupying_process "$mountpoint"
		sync
		umount "$mountpoint"
		exit_code="$?"
		[ "$exit_code" -eq 0 ] && {
			echo "force-unmount: ${mountpoint} successfully unmounted" >&2
			exit 0
		}
	done
	echo "force-unmount: can't unmount ${mountpoint}" >&2
	exit "$exit_code"
else
	echo "force-unmount: ${mountpoint} is not mounted" >&2
	exit 1
fi
